/*=======================================================================
 *Subsystem:   no-real-time
 *File:        task.c
 *Author:      ZhangWenbo
 *Description:
 ========================================================================
 * History:
 * 1. Date: 2023.10.09
      Author:zhangwenbo
      Modification: 1. delete taskcount++ of TaskIdle and TaskStat.

 * 2. Date: 2023.10.13
      Author:zhangwenbo
      Modification: 1. add feed dog task to TimeTick function, 400ms one time;

 * 3. Date: 2023.10.16
      Author:zhangwenbo
      Modification: 1. OSTime from uint32_t to uint64_t.
========================================================================*/
#include "task.h"
#include "type.h"
#include<string.h>
#include<stdio.h>

extern void FeedIWdg(void);

static const uint8_t OSUnMapTbl[256] = {
	0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x00 to 0x0F                             */
	4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x10 to 0x1F                             */
	5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x20 to 0x2F                             */
	4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x30 to 0x3F                             */
	6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x40 to 0x4F                             */
	4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x50 to 0x5F                             */
	5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x60 to 0x6F                             */
	4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x70 to 0x7F                             */
	7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x80 to 0x8F                             */
	4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x90 to 0x9F                             */
	5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xA0 to 0xAF                             */
	4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xB0 to 0xBF                             */
	6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xC0 to 0xCF                             */
	4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xD0 to 0xDF                             */
	5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xE0 to 0xEF                             */
	4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0        /* 0xF0 to 0xFF                             */
};
static const uint8_t OSMapTbl[8] = {1, 2, 4, 8, 16, 32, 64, 128};

uint8_t OSRdyGrp = 0;                        /* task group flag */

uint8_t OSRdyTbl[8] = {0, 0, 0, 0, 0, 0, 0, 0}; /* task state flag */

uint8_t OSRuning;            /* start running flag */

uint8_t OSTaskNum;           /* task number */

uint64_t OSTime;             /* absolute time, uint: ms */

uint32_t OSIdleCtrMax;       /* idle task counter per second */

uint32_t OSIdleCtr;          /* idle task counter */

uint8_t OSCPUUsage;          /*  real cpu output coefficient  */

uint8_t OSMAXCPUUsage;       /* maximum cpu output coefficient */

TASK task[MAXNUMTASK];
/*=======================================================================
 *function: uint8_t TaskRoll(void)
 *parameter:
 *return: judge task is vaild
 *describe: task execute
========================================================================*/
uint8_t TaskRoll(void) {
	uint8_t x, y, prio;

	y = OSUnMapTbl[OSRdyGrp];
	x = OSUnMapTbl[OSRdyTbl[y]];
	prio = y * 8 + x;                  /* find high priority task */

	if ( prio >= MAXNUMTASK || prio == NOTASK_PRIO) {
		return TASKPRIO_INVALID;
	} else {
		task[prio].handle();           /* execute task */
		task[prio].taskcount++;
		if ( prio != IDLETASK_PRIO) {  /* not delete idle task flag*/
			if ((OSRdyTbl[prio >> 3] = OSRdyTbl[prio >> 3] & (~OSMapTbl[prio & 0x07])) == 0) { /* It needs to delete  */
				OSRdyGrp = OSRdyGrp&(~OSMapTbl[prio >> 3]);
			}
		}
		task[prio].timecount = task[prio].masktime;
	}
	return TASK_NOERROR;
}
/*=======================================================================
 *function: void TimeTick(void)
 *parameter:
 *return:
 *describe: task timer, place it in time tick interrupt function.
========================================================================*/
void TimeTick(void) {
	uint8_t prio;

	OSTime++;

	if ( OSRuning == TASKSTOP ) {
		return;
	}
	for (prio = 0; prio < MAXNUMTASK; prio++ ) { /* task time is zero*/
		if ( task[prio].flag == 1 ) {
			if ( task[prio].offtimecount == 0 ) {
				if (task[prio].timecount != 0) {
					task[prio].timecount--;
				}
				if (task[prio].timecount == 0) {
					if ( task[prio].status == OS_STAT_RDY ) { /* task enter ready list table */
						OSRdyGrp = OSRdyGrp | OSMapTbl[prio >> 3];
						OSRdyTbl[prio >> 3] = OSRdyTbl[prio >> 3] | OSMapTbl[prio & 0x07];
					}
				}
			} else {
				task[prio].offtimecount--;
			}
		}
	}
	return;
}
/*=======================================================================
 *function: uint8_t TaskCreate(void (*Handle)(), uint8_t prio, uint16_t reftime, uint16_t offtime)
 *parameter: Handle: task function, prio: task priority, perTime: task period, offTime: offset time.
 *return: task state
 *describe: task create
========================================================================*/
uint8_t TaskCreate(void (*Handle)(), uint8_t prio, uint16_t perTime, uint16_t offTime) {
	if (prio >= MAXNUMTASK || prio == NOTASK_PRIO) {
		return TASKPRIO_INVALID;
	}

	if (task[prio].flag == 1) {
		return TASKPRIO_USED;
	} else {
		task[prio].handle = Handle;
		task[prio].flag = 1;
		task[prio].masktime = perTime / TIMETICK;
		task[prio].maskofftime = offTime / TIMETICK;
		task[prio].offtimecount = offTime / TIMETICK;
		task[prio].timecount = perTime / TIMETICK;
		task[prio].status = OS_STAT_RDY;
		OSTaskNum++;
	}
	return TASK_NOERROR;
}
/*=======================================================================
 *function: uint8_t TaskResume (uint8_t prio)
 *parameter: prio: task priority
 *return: task state
 *describe: resume task from suspend
========================================================================*/
uint8_t TaskResume(uint8_t prio) {

	if ( prio >= MAXNUMTASK || prio == NOTASK_PRIO || prio == IDLETASK_PRIO) {
		return TASKPRIO_INVALID;
	}
	if (task[prio].flag == 0) {
		return TASKPRIO_NOCREAT;
	}
	task[prio].status &= OS_STAT_MASKSUSPEND; /* clear suspend state */

	if (task[prio].status == OS_STAT_RDY) { /* if the task is ready state, enter ready list table */
		OSRdyGrp = OSRdyGrp | OSMapTbl[prio >> 3];
		OSRdyTbl[prio >> 3] = OSRdyTbl[prio >> 3] | OSMapTbl[prio & 0x07];
	}

	return TASK_NOERROR;
}
/*=======================================================================
 *function: uint8_t TaskSuspend(uint8_t prio)
 *parameter: prio: task priority
 *return: task state
 *describe: task suspend
========================================================================*/
uint8_t TaskSuspend(uint8_t prio) {

	if ( prio >= MAXNUMTASK || prio == NOTASK_PRIO || prio == IDLETASK_PRIO) {
		return TASKPRIO_INVALID;
	}
	if (task[prio].flag == 0) {
		return TASKPRIO_NOCREAT;
	}
	task[prio].status |= OS_STAT_SUSPEND;

	if ((OSRdyTbl[prio >> 3] = OSRdyTbl[prio >> 3] & (~OSMapTbl[prio & 0x07])) == 0) { /* if the task is ready state, delete the flag */
		OSRdyGrp = OSRdyGrp&(~OSMapTbl[prio >> 3]);
	}

	return TASK_NOERROR;
}
/*=======================================================================
 *function: uint8_t TaskDel(uint8_t prio)
 *parameter: prio: task priority
 *return: task state
 *describe: task delete
========================================================================*/
uint8_t TaskDel(uint8_t prio) {
	if (prio >= MAXNUMTASK || prio == NOTASK_PRIO || prio == IDLETASK_PRIO) {
		return TASKPRIO_INVALID;
	}

	if (task[prio].flag == 0) {
		return TASKPRIO_NOCREAT;
	}

	if ((OSRdyTbl[prio >> 3] = OSRdyTbl[prio >> 3] & (~OSMapTbl[prio & 0x07])) == 0) { /* delete task flag list table, no matter what it is ready */
		OSRdyGrp = OSRdyGrp&(~OSMapTbl[prio >> 3]);
	}

	(void)memset(&task[prio], 0, sizeof(TASK));    /* clean task struct */
	OSTaskNum--;

	return TASK_NOERROR;
}
/*=======================================================================
 *function: uint8_t  TaskChangePrio(uint8_t oldprio, uint8_t newprio)
 *parameter: oldprio: old priority, newprio: change priority
 *return: task state
 *describe: change task priority
========================================================================*/
uint8_t  TaskChangePrio(uint8_t oldprio, uint8_t newprio) {
	uint8_t err;

	if (newprio >= MAXNUMTASK || oldprio >= MAXNUMTASK \
	    || newprio == NOTASK_PRIO || oldprio == NOTASK_PRIO \
	    || newprio == IDLETASK_PRIO || oldprio == IDLETASK_PRIO) {
		return TASKPRIO_INVALID;
	}

	if (task[newprio].flag == 1) { /* judge wether priority is use */
		return TASKPRIO_USED;
	}
	task[newprio] = task[oldprio];   /* change task priority */
	OSTaskNum++;

	err = TaskDel(oldprio);
	if (err != TASK_NOERROR) {
		return err;
	}
	return TASK_NOERROR;
}
/*=======================================================================
 *function: void TaskStat(void)
 *parameter:
 *return:
 *describe: cpu output coeffcient
========================================================================*/
void TaskStat(void) {
	uint32_t     run;
	uint32_t     max;
	uint32_t     usage = 0;

	if ( OSIdleCtrMax == 0 ) {
		OSIdleCtrMax = OSIdleCtr;
		AppTaskCreate();
	}

	max = OSIdleCtrMax / 100;
	run = OSIdleCtr;
	OSIdleCtr = 0;
	if (max > 0) {
		if (run / max > 100) {           /* prevent negative number */
			usage = 0;
		} else {
			usage = (uint8_t)(100 - run / max);
		}
		if (usage > 0) {
			OSCPUUsage = (uint8_t)usage;
		} else {
			OSCPUUsage = 0;
		}
	} else {
		OSCPUUsage = 0;
	}

#if SYSRUN_MODEL == DEBUG          /* in debug mode, observe the maximum cpu unilization ratio */
	if ( OSCPUUsage > OSMAXCPUUsage) {
		OSMAXCPUUsage = OSCPUUsage;
	}
#endif

	return;
}
/*=======================================================================
 *function: void TaskIdle (void)
 *parameter:
 *return:
 *describe: idle task
========================================================================*/
void TaskIdle (void) {
	OSIdleCtr++;           /*idle task counter */
	return;
}
/*=======================================================================
 *function: void TaskParaInit(void)
 *parameter:
 *return:
 *describe: task parameter initialize
========================================================================*/
void TaskParaInit(void) {
	OSRuning = TASKRUN;
	OSTaskNum = 0;
	OSTime = 0;
	OSIdleCtrMax = 0;
	OSCPUUsage = 0;
	OSIdleCtr = 0;
	OSCPUUsage = 0;

#if SYSRUN_MODEL == DEBUG
	OSMAXCPUUsage = 0;
#endif

	(void)memset(task, 0, sizeof(TASK)*MAXNUMTASK);
	OSRdyGrp = OSMapTbl[IDLETASK_PRIO >> 3];
	OSRdyTbl[IDLETASK_PRIO >> 3] = OSRdyTbl[IDLETASK_PRIO >> 3] | OSMapTbl[IDLETASK_PRIO & 0x07];

	return;
}
/*=======================================================================
========================================================================*/
